package com.moonshine.parser.finder;

import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import org.sqlite.SQLiteConfig;
import org.sqlite.SQLiteJDBCLoader;

import com.moonshine.parser.models.ClassBaseSignature;
import com.moonshine.parser.models.ClassSignature;
import com.moonshine.parser.models.InterfaceSignature;
import com.moonshine.parser.models.MethodSignature;
import com.moonshine.parser.models.VariableSignature;
import com.moonshine.utils.InfoDBUtils;
import com.moonshine.utils.SignatureTools;

public class SQLiteInfoDB extends ClassInfoDB {

	private Connection conn;
	private List<Integer> processedClassCheckSums = new ArrayList<Integer>();

	public SQLiteInfoDB(String name) {
		super();
		System.out.println(String.format("running in %s mode", SQLiteJDBCLoader
				.isNativeMode() ? "native" : "pure-java"));

		// TODO Auto-generated constructor stub
		SQLiteConfig config = new SQLiteConfig();
		config.enforceForeignKeys(true);

		// load the sqlite-JDBC driver using the current class loader
		try {
			Class.forName("org.sqlite.JDBC");

			conn = DriverManager.getConnection("jdbc:sqlite:" + name, config
					.toProperties());

		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	protected void processClassSignature(ClassBaseSignature classSig) {
		int classCheckSum = SignatureTools.getClassCRCValue(classSig);
		if (processedClassCheckSums.indexOf(classCheckSum) != -1)
			return;
		int classID = -1;
		try {
			classID = InfoDBUtils.findClassID(conn, classSig);
			if (classID == -1) {
				classID = InfoDBUtils.insertClassSignature(conn, classSig);

			}

		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		if (classID == -1) {
			return;
		}
		// InfoDBUtils.deleteSignature(classSig, classID, true);

		// TODO : Insert variables

		/*
		 * List<VariableSignature> missingVarSigs = InfoDBUtils
		 * .findMissingVariableSignatures(conn, classSig);
		 */
		final Boolean isClassSignature = classSig instanceof ClassSignature;
		List<String> extendedFrom = new ArrayList<String>();

		if (isClassSignature) {
			String extendedClass = ((ClassSignature) classSig).extendedFrom;
			if (extendedClass != null && extendedClass != "")
				extendedFrom.add(extendedClass);
		} else {

			extendedFrom = ((InterfaceSignature) classSig).extendedFrom;
		}

		// Index extended class/interface first
		for (String extendedClass : extendedFrom) {
			File extendedClassFile = findClassFilePath(extendedClass);
			if (extendedClassFile != null)
				indexClassFile(extendedClassFile);
		}

		if (isClassSignature)
			insertClassVariableSignatures(classID, (ClassSignature) classSig);

		// TODO : Insert methods
		insertMethodSignatures(classID, classSig);

		// TODO : Insert method variables
		/*
		 * Map<MethodSignature,List<VariableSignature>> missingMethodVarSigs =
		 * InfoDBUtils.findMissingVariableSignatures(conn, missingMethodSigs);
		 * 
		 * if(missingMethodVarSigs.size()!=0){
		 * 
		 * for(MethodSignature methodSig :missingMethodVarSigs.keySet()){
		 * missingVarSigs = missingMethodVarSigs.get(methodSig);
		 * insertMethodVariableSignatures(methodSig,missingVarSigs); }
		 * 
		 * }
		 */

		// TODO : Insert Interfaces

		// TODO : Insert interaces methods

		// TODO : Insert interfaces methods variables

		// TODO Auto-generated method stub
		// Check to see if we have any dependences and resolve those
		// first
		// By trying to find the file from the classPath collection
		// be sure to insert the date modified for checking if need to
		// update

		// we will use the imports of .* to help speed up the resolving
		// List<String> deps = new ArrayList<String>();
		//
		// if (deps.size() != 0) {
		// List<File> resolvedDependices = new ArrayList<File>();
		// int classPathLength = classPaths.length;
		// for (String depClass : deps) {
		// for (int i = 0; i < classPathLength; i++) {
		// FileUtils.findFilesRecursive(resolvedDependices,
		// new File(classPaths[i]));
		// }
		// }
		//
		// }

		// If no dependences or all have been inserted go ahead and
		// insert
		// this class signature
		processedClassCheckSums.add(classCheckSum);
	}

	private void insertClassVariableSignatures(int classID,
			ClassSignature classSig) {
		// TODO Auto-generated method stub

		List<VariableSignature> varSigs = classSig.variables;
		if (varSigs.size() == 0)
			return;
		List<ClassSignature> classSigs = null;
		PreparedStatement stmt;
		try {
			stmt = conn
					.prepareStatement("DELETE FROM class_has_variables where class_id = ?");
			stmt.setInt(1, classID);
			stmt.execute();
		} catch (SQLException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		insertMissingClassesFromVariables(classSig, varSigs);

		try {
			InfoDBUtils.insertClassVariableSignatures(conn, classID, varSigs);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	private void insertMissingClassesFromVariables(ClassBaseSignature classSig,
			List<VariableSignature> varSigs) {
		// TODO Auto-generated method stub
		// make sure we have the qualifed className for the variables
		try {
			resolveClassVariableTypes(classSig, varSigs);
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		List<ClassSignature> missingClassSignatures = null;
		try {
			missingClassSignatures = InfoDBUtils.findMissingClasses(conn,
					varSigs);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// found some missing signatures lets index thoses
		if (missingClassSignatures != null
				&& missingClassSignatures.size() != 0) {
			File foundMissingClassFile;
			for (ClassSignature missingClassSig : missingClassSignatures) {
				foundMissingClassFile = findClassFilePath(missingClassSig);
				if (foundMissingClassFile != null)
					indexClassFile(foundMissingClassFile);
			}
		}
	}

	protected void insertMethodSignatures(int classID,
			ClassBaseSignature classSig) {
		// TODO Auto-generated method stub

		List<MethodSignature> methodSigs = classSig.methods;
		if (methodSigs.size() == 0)
			return;
		List<ClassSignature> classSigs = null;
		Statement stmt;
		try {
			stmt = conn.createStatement();
			stmt.execute("DELETE FROM class_has_methods where class_id ="
					+ classID);
			
			stmt.execute("DELETE FROM method_has_variables where class_id = "
					+ classID);

		} catch (SQLException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		int returnTypeClassID;
		int methodID = 0;
		File foundReturnTypeFile;
		PreparedStatement pstmt = null;
		ResultSet rs;
		try {
			pstmt = conn
					.prepareStatement(
							"INSERT INTO class_methods (method_name,return_class_type_id,visibility,access)"
									+ " VALUES(?,?,?,?)",
							Statement.RETURN_GENERATED_KEYS);
		} catch (SQLException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		List<Integer> methodIDs = new ArrayList<Integer>();
		for (MethodSignature methodSig : methodSigs) {

			if (methodSig.returnType != "void" && methodSig.returnType != "*") {

				if(classSig.toString().equals("flash.data:flash.data:SQLConnection")){
					classSig = classSig;
				}
				try {
					methodSig.returnType = resolveClassVariableTypes(
							classSig, methodSig.returnType);
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}

				try {
					returnTypeClassID = InfoDBUtils.findClassID(conn,
							methodSig.returnType);
					// not indexed so index file
					if (returnTypeClassID == -1) {
						indexClassFile(findClassFilePath(classSig,methodSig.returnType));
						// hopefull insert work so get the classID

						returnTypeClassID = InfoDBUtils.findClassID(conn,
								methodSig.returnType);
						if (returnTypeClassID == -1) {
							continue;
							/*throw new Exception("BITCH :: "
									+ methodSig.returnType);*/
						}
					}

					pstmt.setString(1, methodSig.name);
					pstmt.setInt(2, returnTypeClassID);
					pstmt.setInt(3, methodSig.flags);
					pstmt.setInt(4, methodSig.access);
					pstmt.execute();
					// if (pstmt.execute()) {
					rs = pstmt.getGeneratedKeys();
					if (rs.next()) {
						methodID = rs.getInt(1);
						methodIDs.add(methodID);
						insertMissingClassesFromVariables(classSig,
								methodSig.paramaters);

						InfoDBUtils.insertMethodVariableSignatures(conn,
								methodID, returnTypeClassID,
								methodSig.paramaters);
					}

					// }
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

			}
		}
		try {
			InfoDBUtils.insertClassMethods(conn,classID,methodIDs);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
}
